package com.hunttown.common.dao.datasource.config;

import com.hunttown.common.dao.sharding.ShardingRouters;
import com.hunttown.common.sharding.client.config.ShardConfig;
import com.hunttown.common.sharding.client.config.TableConfig;
import com.hunttown.common.sharding.client.datasource.DefaultMultiDataSourcesService;
import com.hunttown.common.sharding.client.router.SimpleRouter;
import com.hunttown.common.sharding.client.rule.SimpleRouteRule;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

public class ShardingBeanDefinitionParser implements BeanDefinitionParser {
    private static final Logger logger = LoggerFactory.getLogger(MasterSlaveDataSourceBeanDefinitionParser.class);
    private Class DATASOURCES_SERVICE_CLASS;
    private Class SIMPLE_ROUTER_CLASS;
    private Class SHARD_CONFIG_CLASS;
    private Class TABLE_CONFIG_CLASS;
    private Class SIMPLE_ROUTE_RULE_CLASS;
    private Class SHARD_ROUTERS_CLASS;

    public ShardingBeanDefinitionParser() {
        try {
            this.DATASOURCES_SERVICE_CLASS = DefaultMultiDataSourcesService.class;
            this.SIMPLE_ROUTER_CLASS = SimpleRouter.class;
            this.SHARD_CONFIG_CLASS = ShardConfig.class;
            this.TABLE_CONFIG_CLASS = TableConfig.class;
            this.SIMPLE_ROUTE_RULE_CLASS = SimpleRouteRule.class;
            this.SHARD_ROUTERS_CLASS = ShardingRouters.class;
        } catch (Throwable var2) {
            ;
        }

    }

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        logger.debug("sharding-config : ==========start sharding config==========");
        if (this.DATASOURCES_SERVICE_CLASS == null) {
            throw new IllegalStateException("please add db sharding dependency");
        } else {
            List<Element> dataSourcesServiceElements = DomUtils.getChildElementsByTagName(element, "sharding-datasources-service");
            this.parseDataSourcesServices(dataSourcesServiceElements, parserContext);
            List<Element> simpleRuleElements = DomUtils.getChildElementsByTagName(element, "sharding-simple-rule");
            this.parseSimpleRules(simpleRuleElements, parserContext);
            List<Element> routerElements = DomUtils.getChildElementsByTagName(element, "sharding-simple-router");
            this.parseSimpleRouters(routerElements, parserContext);
            logger.debug("sharding-config : ==========end sharding config==========");
            return null;
        }
    }

    private void parseDataSourcesServices(List<Element> dataSourcesServiceElements, ParserContext parserContext) {
        Iterator i$ = dataSourcesServiceElements.iterator();

        while (i$.hasNext()) {
            Element dataSourcesServiceElement = (Element) i$.next();
            String beanName = dataSourcesServiceElement.getAttribute("id");
            logger.debug("sharding-datasources-service config : bean name : {}", beanName);
            List<Element> datasourceElements = DomUtils.getChildElementsByTagName(dataSourcesServiceElement, "sharding-datasource");
            Map<String, String> dataSourceMap = this.parseShardingDatasources(datasourceElements, parserContext);
            RootBeanDefinition dataSourcesServiceDefinition = new RootBeanDefinition(this.DATASOURCES_SERVICE_CLASS);
            dataSourcesServiceDefinition.getPropertyValues().add("dataSourcePool", dataSourceMap);
            parserContext.getRegistry().registerBeanDefinition(beanName, dataSourcesServiceDefinition);
        }

    }

    private Map<String, String> parseShardingDatasources(List<Element> datasourceElements, ParserContext parserContext) {
        ManagedMap<String, String> dataSourceMap = new ManagedMap();
        Iterator i$ = datasourceElements.iterator();

        while (i$.hasNext()) {
            Element datasourceElement = (Element) i$.next();
            String key = datasourceElement.getAttribute("db-index-name");
            String datasourceBeanName = datasourceElement.getAttribute("datasource-ref");
            logger.debug("sharding-datasources-service config : db-index-name : {}, datasource-ref : {}", key, datasourceBeanName);
            if (dataSourceMap.containsKey(key)) {
                throw new IllegalArgumentException("sharding-datasources-service config : db-index-name [" + key + "] must not repeat.");
            }

            if (!parserContext.getRegistry().containsBeanDefinition(datasourceBeanName)) {
                throw new IllegalArgumentException("sharding-datasources-service config : db-index-name [" + key + "] reference an not exists datasource [" + datasourceBeanName + "].");
            }

            dataSourceMap.put(key, datasourceBeanName);
        }

        return dataSourceMap;
    }

    private void parseSimpleRules(List<Element> simpleRuleElements, ParserContext parserContext) {
        Iterator i$ = simpleRuleElements.iterator();

        while (i$.hasNext()) {
            Element simpleRuleElement = (Element) i$.next();
            String beanName = simpleRuleElement.getAttribute("id");
            String dbsCount = simpleRuleElement.getAttribute("db-count");
            String tablesCount = simpleRuleElement.getAttribute("table-count");
            logger.debug("sharding-simple-rule config : bean name : {}, db-count : {}, table-count : {}", new Object[]{beanName, dbsCount, tablesCount});
            RootBeanDefinition simpleRuleDefinition = new RootBeanDefinition(this.SIMPLE_ROUTE_RULE_CLASS);
            simpleRuleDefinition.getConstructorArgumentValues().addGenericArgumentValue(dbsCount);
            simpleRuleDefinition.getConstructorArgumentValues().addGenericArgumentValue(tablesCount);
            parserContext.getRegistry().registerBeanDefinition(beanName, simpleRuleDefinition);
        }

    }

    private void parseSimpleRouters(List<Element> routerElements, ParserContext parserContext) {
        ManagedMap<String, RuntimeBeanReference> routerMap = new ManagedMap();
        Iterator i$ = routerElements.iterator();

        while (i$.hasNext()) {
            Element routerElement = (Element) i$.next();
            String routerBeanName = routerElement.getAttribute("id");
            logger.debug("sharding-simple-router : bean name : {}", routerBeanName);
            List<Element> tableConfigElements = DomUtils.getChildElementsByTagName(routerElement, "sharding-table-config");
            Iterator k$ = tableConfigElements.iterator();

            while (k$.hasNext()) {
                Element tableConfigElement = (Element) k$.next();
                routerMap.put(tableConfigElement.getAttribute("logical-table-name"), new RuntimeBeanReference(routerBeanName));
            }

            ManagedMap<String, RuntimeBeanReference> tableConfigMap = this.parseTableConfigs(routerBeanName, tableConfigElements, parserContext);
            RootBeanDefinition shardConfigDefinition = new RootBeanDefinition(this.SHARD_CONFIG_CLASS);
            shardConfigDefinition.getPropertyValues().add("tableConfig", tableConfigMap);
            String shardConfigBeanName = BeanDefinitionReaderUtils.generateBeanName(shardConfigDefinition, parserContext.getRegistry());
            parserContext.getRegistry().registerBeanDefinition(shardConfigBeanName, shardConfigDefinition);
            RootBeanDefinition simpleRouterDefinition = new RootBeanDefinition(this.SIMPLE_ROUTER_CLASS);
            simpleRouterDefinition.getPropertyValues().add("shardConfig", new RuntimeBeanReference(shardConfigBeanName));
            parserContext.getRegistry().registerBeanDefinition(routerBeanName, simpleRouterDefinition);
        }

        RootBeanDefinition shardingRoutersDefinition = new RootBeanDefinition(this.SHARD_ROUTERS_CLASS);
        shardingRoutersDefinition.getPropertyValues().add("routerMap", routerMap);
        parserContext.getRegistry().registerBeanDefinition("shardingRouters", shardingRoutersDefinition);
    }

    private ManagedMap<String, RuntimeBeanReference> parseTableConfigs(String routerBeanName, List<Element> tableConfigElements, ParserContext parserContext) {
        ManagedMap<String, RuntimeBeanReference> tableConfigMap = new ManagedMap();
        Iterator i$ = tableConfigElements.iterator();

        while (i$.hasNext()) {
            Element tableConfigElement = (Element) i$.next();
            String logicalTableName = tableConfigElement.getAttribute("logical-table-name");
            String dbStartWith = tableConfigElement.getAttribute("db-start-with");
            String dbIndexPrefix = tableConfigElement.getAttribute("db-index-prefix");
            String dbIndexSuffix = tableConfigElement.getAttribute("db-index-suffix");
            String dbCount = tableConfigElement.getAttribute("db-count");
            String tableStartWith = tableConfigElement.getAttribute("table-start-with");
            String tableNamePrefix = tableConfigElement.getAttribute("table-name-prefix");
            String tableNameSuffix = tableConfigElement.getAttribute("table-name-suffix");
            String tableCount = tableConfigElement.getAttribute("table-count");
            String routeField = tableConfigElement.getAttribute("route-field");
            String tableNameField = tableConfigElement.getAttribute("table-name-field");
            String routeRuleBeanName = tableConfigElement.getAttribute("route-rule-ref");
            String dataSourcesServiceBeanName = tableConfigElement.getAttribute("datasources-service-ref");
            String needPadToAlign = tableConfigElement.getAttribute("need-pad-to-align");
            logger.debug("sharding-table-config : logical-table-name : {}, db-index-prefix : {}, db-index-suffix : {}, db-start-with : {}, db-count : {}, table-name-prefix : {}, table-name-suffix : {}, table-start-with : {}, table-count : {}, route-field : {}, table-name-field : {}, route-rule-ref : {}, need-pad-to-align : {}, datasources-service-ref : {}", new Object[]{logicalTableName, dbStartWith, dbIndexPrefix, dbIndexSuffix, dbCount, tableStartWith, tableNamePrefix, tableNameSuffix, tableCount, routeField, tableNameField, routeRuleBeanName, needPadToAlign, dataSourcesServiceBeanName});
            if (StringUtils.isBlank(logicalTableName)) {
                throw new IllegalArgumentException("sharding-table-config config : under the [" + routerBeanName + "] sharding-simple-router tag of " + "the sharding-table-config tag, logical-table-name must not null.");
            }

            if (tableConfigMap.containsKey(logicalTableName)) {
                throw new IllegalArgumentException("sharding-table-config config : under the [" + routerBeanName + "] sharding-simple-router tag of " + "the sharding-table-config tag, [" + logicalTableName + "] logical-table-name must not repeat.");
            }

            if (!parserContext.getRegistry().containsBeanDefinition(routeRuleBeanName)) {
                throw new IllegalArgumentException("sharding-table-config config : under the [" + routerBeanName + "] sharding-simple-router tag of " + "the sharding-table-config tag, [" + routeRuleBeanName + "] route-rule-ref bean dont`s exists.");
            }

            if (!parserContext.getRegistry().containsBeanDefinition(dataSourcesServiceBeanName)) {
                throw new IllegalArgumentException("sharding-table-config config : under the [" + routerBeanName + "] sharding-simple-router tag of " + "the sharding-table-config tag, [" + dataSourcesServiceBeanName + "] datasources-service-ref bean dont`s exists.");
            }

            RootBeanDefinition tableConfigDefinition = new RootBeanDefinition(this.TABLE_CONFIG_CLASS);
            MutablePropertyValues propertyValues = tableConfigDefinition.getPropertyValues();
            propertyValues.add("dbIndexPrefix", dbIndexPrefix);
            propertyValues.add("dbIndexSuffix", dbIndexSuffix);
            propertyValues.add("dbStartWith", dbStartWith);
            propertyValues.add("dbCount", dbCount);
            propertyValues.add("tbNamePrefix", tableNamePrefix);
            propertyValues.add("tbNameSuffix", tableNameSuffix);
            propertyValues.add("tableStartWith", tableStartWith);
            propertyValues.add("tableCount", tableCount);
            propertyValues.add("routeField", routeField);
            propertyValues.add("tabNameField", tableNameField);
            propertyValues.add("needPadToAlign", needPadToAlign);
            propertyValues.add("routeRule", new RuntimeBeanReference(routeRuleBeanName));
            propertyValues.add("dataSourcesService", new RuntimeBeanReference(dataSourcesServiceBeanName));
            String tableConfigBeanName = BeanDefinitionReaderUtils.generateBeanName(tableConfigDefinition, parserContext.getRegistry());
            parserContext.getRegistry().registerBeanDefinition(tableConfigBeanName, tableConfigDefinition);
            tableConfigMap.put(logicalTableName, new RuntimeBeanReference(tableConfigBeanName));
        }

        return tableConfigMap;
    }
}